const path = require("path");
// 提供修改ast节点的方法
const types = require('@babel/types')
const generator = require('@babel/generator').default
const traverse = require("@babel/traverse").default


class NormalModule {
    constructor(data) {
        // process.cwd()
        this.context = data.context
        this.name = data.name
        this.moduleId = data.moduleId
        // this.entry = data.entry
        this.rawRequest = data.rawRequest
        this.parser = data.parser
        this.resource = data.resource // 文件绝对路径
        this._source // 存放某个模块的源代码
        this._ast // 存放某个模块源代码的 ast
        this.dependencies = [] // 定义一个空数组保存被依赖加载的模块信息
    }

    build(compilation, callback) {
        this.doBuild(compilation, (err) => {
            this._ast = this.parser.parse(this._source)

            // 修改ast
            traverse(this._ast, {
                CallExpression: (nodePath) => {
                    // 拿到ast上的node
                    let node = nodePath.node

                    // 定位require所在节点
                    if (node.callee.name === 'require') {
                        // 获取原始请求路径
                        let modulePath = node.arguments[0].value // './title'
                        // 取出当前被加载的模块名称(根据分隔符拆分)
                        // path.posix.sep -> 拿到当前操作系统的分隔符
                        let moduleName = modulePath.split(path.posix.sep).pop() // title
                        // 当前我们只能打包处理js
                        // moduleName有 . 表示有指定后缀, 没有就默认指定为js
                        let extName = moduleName.indexOf('.') === -1 ? '.js' : ''
                        moduleName += extName // title.js
                        // 最终要有绝对路径, 以便读取文件
                        // 拼接当前文件的父目录 和 导入文件名
                        let depResource = path.posix.join(path.posix.dirname(this.resource), moduleName)
                        // 定义当前模块的id
                        // this.context - depResource -> ./src/title.js
                        let depModuleId = './' + path.posix.relative(this.context, depResource)

                        // 记录当前被依赖模块的信息, 方便后面加载
                        this.dependencies.push({
                            name: this.name, // todo 动态修改
                            context: this.context,
                            rawRequest: moduleName,
                            moduleId: depModuleId,
                            resource: depResource
                        })

                        // 替换内容
                        node.callee.name = '__webpack_require__'
                        // types.stringLiteral 创建string类型的值
                        // node.arguments 替换为 depModuleId (string)
                        node.arguments = [types.stringLiteral(depModuleId)]
                    }
                }
            })

            // 将修改后的ast转为code(可执行代码)
            let {code} = generator(this._ast)
            this._source = code
            callback(err)
        })
    }

    doBuild(compilation, callback) {
        this.getSource(compilation, (err, source) => {
            this._source = source
            callback()
        })
    }

    getSource(compilation, callback) {
        compilation.inputFileSystem.readFile(this.resource, 'utf-8', callback)
    }
}

module.exports = NormalModule